import {def, del, set} from "../observer/index";
import {hasOwn, isDef, isObject, isPlainObject} from "../../shared/util";
import {observe} from "../observer/observer";


export function stateMixin (Reactive) {

    Reactive.prototype.$data = undefined;

    Reactive.prototype.$props = undefined;

    Reactive.prototype.$set = set;

    Reactive.prototype.$del = del;

    Reactive.prototype.$watch = function () {};

}

export function initState (vm) {
    initProps(vm);
    initData(vm);
    initMethod(vm);
}

function proxyData (target, key, source) {
    const descriptor = {
        enumerable: true,
        configurable: true,
        get: function () {
            return source[key];
        },
        set: function (value) {
            source[key] = value;
        }
    };
    Object.defineProperty(
        target,
        key,
        descriptor
    );
}

export function initProps (vm) {
    // 如果定义了 props 属性
    if (isDef(vm.$options.props)) {
        // props 都是对象类型 <key, value> 键值对
        // props 中的键的引用是固定的，不可修改的
        // 但如果传递的是对象, 对象内部是可以修改的
        vm._props = {};
        Object.keys(vm.$options.props).forEach(function (property) {
            def(vm._props, property, vm.$options.props[property]);
            proxyData(vm, property, vm._props);
        });
    }
}

export function initData (vm) {
    // 如果定义了 data 属性
    if (isDef(vm.$options.data)) {
        // 获取数据，只有两种类型，对象类型，函数类型
        vm._data = isPlainObject(vm.$options.data)
            ? vm.$options.data
            : vm.$options.data.call(vm);
        // 遍历 data, 将数据访问由 _data 代理
        // 这样可以直接使用 this 来进行访问
        Object.keys(vm._data).forEach(
            function (key) {
                if (!hasOwn(vm, key)) {
                    proxyData(vm, key, vm._data);
                }
            }
        );
        // 对数据进行观察者化
        observe(vm._data);
    }
}

export function initMethod (vm) {
    // 如果定义了 method 属性
    if (isDef(vm.$options.methods)) {
        // 对 method 的属性进行检查
        // 如果 key 已经存在，则不添加该方法
        vm._method = vm.$options.methods;
        Object.keys(vm._method).forEach(
            function (key) {
                if (!hasOwn(vm, key)) {
                    vm[key] = vm._method[key];
                }
            }
        );
    }
}




